home *** CD-ROM | disk | FTP | other *** search
/ Nautilus 1992 July / Nautilus-3-8 / Nautilus-3-8.bin / Tools & Utilities / Techy Stuff / Source ƒ / Task Manager Folder / Task Manager Source / Task.c next >
Encoding:
C/C++ Source or Header  |  1992-05-01  |  13.1 KB  |  565 lines

  1. /*
  2.         Task Manager -- Background processing support
  3.         version 2.2.1
  4.  
  5.     This software source package is Copyright ⌐ 1990-91 by Michael Hecht. All Rights
  6.     Reserved. It may be freely distributed in source or object code format; however,
  7.     the source code may not be sold for profit or charged for in any way. The source
  8.     code must be distributed as a package including all H files, sample code and
  9.     projects, and documentation.
  10.  
  11.     I welcome any comments or suggestions that will help me improve or extend the
  12.     functionality of the Task Manager. You can reach me at:
  13.  
  14.         Internet:        Michael_Hecht@mac.sas.com
  15.         AppleLink:        SAS.HECHT
  16.  
  17.     Happy Tasking!
  18.  
  19.  
  20.     --Michael Hecht
  21.  */
  22.  
  23. #include <GestaltEqu.h>
  24. #include <setjmp.h>
  25. #include "Task.h"
  26.  
  27.  
  28. /* Private Types */
  29.  
  30. typedef struct {
  31.     jmp_buf                envRegisters;
  32.     Handle                envStack;
  33. } TaskEnvironmentRecord;
  34.  
  35. /* Register ordering within the jmp_buf */
  36. enum {
  37.     d3, d4, d5, d6, d7,
  38.     a1, a2, a3, a4, a6, a7
  39. };
  40.  
  41. typedef struct {
  42.     TaskProcPtr                taskProc;
  43.     TaskProcPtr                taskTermProc;
  44.     long                    taskRefCon;
  45.     short                    taskRefNum;
  46.     TaskEnvironmentRecord    taskEnvironment;
  47.     long                    taskFlags;
  48. } TaskRecord, *TaskPtr;
  49.  
  50. /* Values for gTaskMgrFlags */
  51. typedef enum {
  52.     useTempMem =            0x00000001,
  53.     tasksRunning =            0x00000002
  54. } TaskMgrFlags;
  55.  
  56. /* Return values from setjmp; negative values are OSErr's */
  57. enum {
  58.     saveEnvironment, taskResume, taskSuspend
  59. };
  60.  
  61. typedef struct {
  62.     short                    numTasks;
  63.     TaskRecord                theTask[];
  64. } TaskList, **TaskListHandle;
  65.  
  66. static TaskListHandle gTaskList;
  67. static short gTaskAtHand;
  68. static TaskRecord gCurrentTask;
  69. static short gNextTaskRefNum;
  70. static TaskMgrFlags gTaskMgrFlags;
  71. static TaskEnvironmentRecord gAppEnvironment;
  72. static unsigned long gTimeToStop;
  73.  
  74.  
  75.  
  76.  
  77. OSErr InitTasking( void )
  78. {
  79.     OSErr                        err;
  80.     long                        response, tempMask;
  81.  
  82.  
  83.     /* Allocate the task list */
  84.     gTaskList = ( TaskListHandle )NewHandle( sizeof( TaskList ));
  85.     if( !gTaskList )
  86.         return MemError();
  87.  
  88.     /* Initialize global data */
  89.     gTaskAtHand = -1;                /* Run task 0 first */
  90.     ( *gTaskList )->numTasks = 0;
  91.     gNextTaskRefNum = 1;
  92.     gTaskMgrFlags = 0;
  93.  
  94.     /* Determine if temporary memory is available */
  95.     err = Gestalt( gestaltOSAttr, &response );
  96.     if( err == noErr ) {
  97.         tempMask =    ( 1 << gestaltTempMemSupport ) |
  98.                     ( 1 << gestaltRealTempMemory ) |
  99.                     ( 1 << gestaltTempMemTracked );
  100.         if(( response & tempMask ) == tempMask )
  101.             gTaskMgrFlags |= useTempMem;
  102.     }
  103.  
  104.     return noErr;
  105. }
  106.  
  107. OSErr TermTasking( void )
  108. {
  109.     OSErr                err;
  110.     short                taskIndex;
  111.  
  112.  
  113.     /* Can't terminate from a task */
  114.     if( gTaskMgrFlags & tasksRunning )
  115.         return paramErr;
  116.  
  117.     /*
  118.      *    Kill all tasks. We do this from back to front because it's more
  119.      *    efficient, for two reasons:
  120.      *
  121.      *    Ñ    Less memory gets moved when we shrink the task list.
  122.      *
  123.      *    Ñ    The taskIndexes are looked up much faster when starting at
  124.      *        the end of the list.
  125.      */
  126.     err = noErr;
  127.     for( taskIndex = ( *gTaskList )->numTasks - 1; taskIndex >= 0; taskIndex-- ) {
  128.  
  129.         err = DisposeTask(( *gTaskList )->theTask[ taskIndex ].taskRefNum );
  130.         if( err != noErr )
  131.             break;
  132.     }
  133.  
  134.     /* Dispose of the task list */
  135.     DisposHandle(( Handle )gTaskList );
  136.  
  137.     return err;
  138. }
  139.  
  140. short CountTasks( void )
  141. {
  142.     return ( *gTaskList )->numTasks;
  143. }
  144.  
  145. short CurrentTask( void )
  146. {
  147.     return ( gTaskMgrFlags & tasksRunning ) ? gCurrentTask.taskRefNum : 0;
  148. }
  149.  
  150. short GetIndTask( short index )
  151. {
  152.     return ( index < 0 || index >= ( *gTaskList )->numTasks ) ? 0 :
  153.         ( *gTaskList )->theTask[ index ].taskRefNum;
  154. }
  155.  
  156. static short GetTaskIndex( short taskRefNum )
  157. {
  158.     short        taskIndex;
  159.  
  160.  
  161.     /*
  162.      *    Since taskRefNums start at 1 and always increase, we can assume that
  163.      *    the taskIndex will always be less than the taskRefNum or the number of
  164.      *    tasks, which ever is smallest. This makes a good starting point in our
  165.      *    search for the task.
  166.      */
  167.  
  168.     taskIndex = ( *gTaskList )->numTasks;
  169.     if( taskRefNum < taskIndex )
  170.         taskIndex = taskRefNum;
  171.     taskIndex--;
  172.  
  173.     for( ; taskIndex >= 0; taskIndex-- ) {
  174.         if(( *gTaskList )->theTask[ taskIndex ].taskRefNum == taskRefNum )
  175.             break;
  176.     }
  177.  
  178.     /* Note that a negative value will be returned if the task isn't found */
  179.     return taskIndex;
  180. }
  181.  
  182. long GetTaskRefCon( short taskRefNum )
  183. {
  184.     short            taskIndex;
  185.  
  186.  
  187.     taskIndex = GetTaskIndex( taskRefNum );
  188.     if( taskIndex < 0 )
  189.         return 0;
  190.  
  191.     return ( *gTaskList )->theTask[ taskIndex ].taskRefCon;
  192. }
  193.  
  194. OSErr SetTaskRefCon( short taskRefNum, long taskRefCon )
  195. {
  196.     short            taskIndex;
  197.  
  198.  
  199.     taskIndex = GetTaskIndex( taskRefNum );
  200.     if( taskIndex < 0 )
  201.         return paramErr;
  202.  
  203.     ( *gTaskList )->theTask[ taskIndex ].taskRefCon = taskRefCon;
  204.     return noErr;
  205. }
  206.  
  207. static OSErr AllocStack( TaskEnvironmentRecord *theEnvironment, Size stackSize )
  208. {
  209.     /* Has the stack already been allocated? */
  210.     if( theEnvironment->envStack ) {
  211.  
  212.         /* Try to reallocate it */
  213.         ReallocHandle( theEnvironment->envStack, stackSize );
  214.         if( MemError() != noErr )
  215.             return noErr;
  216.  
  217.         /* Dispose of the stack and try allocating a whole new one */
  218.         DisposHandle( theEnvironment->envStack );
  219.     }
  220.  
  221.     /* Try temporary memory first */
  222.     if( gTaskMgrFlags & useTempMem ) {
  223.         OSErr        err;
  224.  
  225.  
  226.         theEnvironment->envStack = TempNewHandle( stackSize, &err );
  227.         if( err == noErr )
  228.             return noErr;
  229.     }
  230.  
  231.     /* If that didn't work, try allocating from the heap */
  232.     theEnvironment->envStack = NewHandle( stackSize );
  233.     return MemError();
  234. }
  235.  
  236. /*
  237.  *    The register declarations in the following routine are quite necessary. The
  238.  *    setjmp call causes THINK C to disable automatic optimizations, so we must
  239.  *    explicitly ask that these variables be placed in registers. It is imperative
  240.  *    that we do so, because these variables cannot survive the other side of the
  241.  *    setjmp until after the stack is restored.
  242.  */
  243. static OSErr SaveEnvironment( register TaskEnvironmentRecord *theEnvironment )
  244. {
  245.     OSErr                err;
  246.     register OSErr        status;
  247.     Ptr                    stackPtr;
  248.     register Size        stackSize;
  249.  
  250.  
  251.     /* Save the registers */
  252.     status = ( OSErr )setjmp( theEnvironment->envRegisters );
  253.     if( status ) {
  254.  
  255.         /* Restore the stack */
  256.         stackSize = GetHandleSize( theEnvironment->envStack );
  257.         BlockMove( *theEnvironment->envStack, CurStackBase - stackSize, stackSize );
  258.         HPurge( theEnvironment->envStack );
  259.         return status;
  260.     }
  261.  
  262.     /* Allocate the stack */
  263.     stackPtr = ( Ptr )theEnvironment->envRegisters[ a7 ];
  264.     stackSize = CurStackBase - stackPtr;
  265.     err = AllocStack( theEnvironment, stackSize );
  266.     if( err != noErr )
  267.         return err;
  268.  
  269.     /* Save the stack */
  270.     HNoPurge( theEnvironment->envStack );
  271.     BlockMove( stackPtr, *theEnvironment->envStack, stackSize );
  272.     return noErr;
  273. }
  274.  
  275. static void RestoreEnvironment( TaskEnvironmentRecord *theEnvironment, OSErr status )
  276. {
  277.     /* Can't let the stack cross into the heap! */
  278.     if(( Ptr )theEnvironment->envRegisters[ a7 ] < HeapEnd ) {
  279. #if TASK_DEBUG
  280.         DebugStr( "\pTaskMgr: Stack overflow" );
  281.         ExitToShell();
  282. #else
  283.         SysError( 28 );
  284. #endif
  285.     }
  286.  
  287. #if TASK_DEBUG
  288.     /* Look for windows in the stack and warn about them */
  289.     {
  290.         WindowPeek        peek;
  291.         Str255            msg, title;
  292.  
  293.  
  294.         for( peek = WindowList; peek; peek = peek->nextWindow ) {
  295.             if(( Ptr )peek < HeapEnd )
  296.                 continue;
  297.  
  298.             GetWTitle(( WindowPtr )peek, title );
  299.             BlockMove( "\pTaskMgr: Window in stack: ", msg, 27 );
  300.             BlockMove( title + 1, msg + msg[ 0 ] + 1, title[ 0 ]);
  301.             msg[ 0 ] += title[ 0 ];
  302.             DebugStr( msg );
  303.             ExitToShell();
  304.         }
  305.     }
  306. #endif
  307.  
  308.     /* Restore the registers */
  309.     longjmp( theEnvironment->envRegisters, ( int )status );
  310. }
  311.  
  312. static void StartNextTask( void )
  313. {
  314.     /* Move to next task at hand */
  315.     gTaskAtHand++;
  316.     if( gTaskAtHand >= ( *gTaskList )->numTasks )
  317.         gTaskAtHand = 0;
  318.  
  319.     /* Keep gCurrentTask up-to-date */
  320.     gCurrentTask = ( *gTaskList )->theTask[ gTaskAtHand ];
  321.  
  322.     /* Start the next task */
  323.     RestoreEnvironment( &gCurrentTask.taskEnvironment, taskResume );
  324.  
  325. #if TASK_DEBUG
  326.     /* This statement should never be hit */
  327.     DebugStr( "\pTaskMgr/StartNextTask: returned from RestoreEnvironment!?!?" );
  328. #endif
  329. }
  330.  
  331. OSErr RunTasks( unsigned long wakeTime )
  332. {
  333.     OSErr                        status;
  334.  
  335.  
  336. #if TASK_DEBUG
  337.     /* Called from task? */
  338.     if( gTaskMgrFlags & tasksRunning ) {
  339.         DebugStr( "\pTaskMgr/RunTasks: Called from task" );
  340.         return paramErr;
  341.     }
  342. #endif
  343.  
  344.     /* Nothing to do if no tasks to run */
  345.     if(( *gTaskList )->numTasks == 0 )
  346.         return noErr;
  347.  
  348.     /* Determine when to stop running tasks */
  349.     gTimeToStop = TickCount();
  350.     gTimeToStop += wakeTime;
  351.  
  352.     /* Save application's state */
  353.     status = SaveEnvironment( &gAppEnvironment );
  354.     switch( status ) {
  355.  
  356.     case saveEnvironment:
  357.         /* We just saved the application's environment; time to start next task */
  358.         StartNextTask();
  359.  
  360.         /* No break needed here because StartNextTask never returns */
  361.  
  362.     case taskSuspend:
  363.         /* Tasks have suspended execution; time to return to the application */
  364.         status = noErr;
  365.         break;
  366.     
  367.     default:
  368.         /* Anything else is an OSErr code */
  369.  
  370.         /* This case will be hit if SaveEnvironment couldn't */
  371. #if TASK_DEBUG
  372.         DebugStr( "\pTaskMgr/RunTasks: Can't save environment" );
  373. #endif
  374.         break;
  375.     }
  376.  
  377.     return status;
  378. }
  379.  
  380. OSErr TaskYield( void )
  381. {
  382.     short                taskAtHand;
  383.     OSErr                status;
  384.     Boolean                timeToSuspend;
  385.     EventRecord            theEvent;
  386.  
  387.  
  388. #if TASK_DEBUG
  389.     /* Called from application? */
  390.     if( !( gTaskMgrFlags & tasksRunning )) {
  391.         DebugStr( "\pTaskMgr/TaskYield: Called from application" );
  392.         return paramErr;
  393.     }
  394. #endif
  395.  
  396.     /*
  397.      *    Determine if it's time to return to the application.
  398.      *
  399.      *    It's that time if the wake time has run out or if the application
  400.      *    received an event.
  401.      */
  402.     timeToSuspend = ( TickCount() >= gTimeToStop ) ||
  403.                     ( EventAvail( everyEvent, &theEvent ));
  404.  
  405.     /* if it's not time to suspend and I'm the only task, then I'll just keep running */
  406.     if( !timeToSuspend && ( *gTaskList )->numTasks == 1 )
  407.         return noErr;
  408.  
  409.     /* Save the current task's environment */
  410.     status = SaveEnvironment( &gCurrentTask.taskEnvironment );
  411.  
  412.     /* Return to the task */
  413.     if( status != saveEnvironment ) {
  414.  
  415.         if( status > noErr )
  416.             status = noErr;
  417. #if TASK_DEBUG
  418.         /* A negative status is an OSErr */
  419.         else
  420.             DebugStr( "\pTaskMgr/TaskYield: Can't save environment" );
  421. #endif
  422.  
  423.         /* Tasks are running now */
  424.         gTaskMgrFlags |=  tasksRunning;
  425.  
  426.         return status;
  427.     }
  428.  
  429.     /* Put the saved environment in the task list */
  430.     ( *gTaskList )->theTask[ gTaskAtHand ].taskEnvironment =
  431.             gCurrentTask.taskEnvironment;
  432.  
  433.     /* If it's time to return to the application, then do so */
  434.     if( timeToSuspend ) {
  435.  
  436.         /* Tasks no longer running */
  437.         gTaskMgrFlags &= ~tasksRunning;
  438.  
  439.         /* Return to the application */
  440.         RestoreEnvironment( &gAppEnvironment, taskSuspend );
  441.     }
  442.  
  443.     /* Start the next task */
  444.     StartNextTask();
  445. }
  446.  
  447. OSErr DisposeTask( short taskRefNum )
  448. {
  449.     short                taskIndex;
  450.     TaskRecord            dyingTask;
  451.     Boolean                harakiri;
  452.  
  453.  
  454.     taskIndex = GetTaskIndex( taskRefNum );
  455.     if( taskIndex < 0 )
  456.         return paramErr;
  457.  
  458.     /* Are we deleting the current task? */
  459.     harakiri = gTaskMgrFlags & tasksRunning && taskIndex == gTaskAtHand;
  460.  
  461.     /* Point to the task record of the task we're disposing */
  462.     dyingTask = ( *gTaskList )->theTask[ taskIndex ];
  463.  
  464.     /* If the task has a term proc, call it now */
  465.     if( dyingTask.taskTermProc )
  466.         ( *dyingTask.taskTermProc )( dyingTask.taskRefCon );
  467.  
  468.     /* We can dispose of its stack now */
  469.     DisposHandle( dyingTask.taskEnvironment.envStack );
  470.  
  471.     /* Remove the task from the task list */
  472.     Munger(( Handle )gTaskList, sizeof( TaskList ) + taskIndex * sizeof( TaskRecord ),
  473.             0, sizeof( TaskRecord ), "", 0 );
  474.  
  475.     /* Fix up task at hand if necessary */
  476.     if( gTaskAtHand >= taskIndex ) {
  477.         --gTaskAtHand;
  478.         if( gTaskAtHand < 0 )
  479.             gTaskAtHand = ( *gTaskList )->numTasks;
  480.     }
  481.  
  482.     /* One less task to keep track of */
  483.     --( *gTaskList )->numTasks;
  484.  
  485.     /* Return to the application if we deleted ourselves */
  486.     if( harakiri ) {
  487.  
  488.         /* Tasks are no longer running (we will be returning to the application) */
  489.         gTaskMgrFlags &= ~tasksRunning;
  490.         RestoreEnvironment( &gAppEnvironment, taskSuspend );
  491.     }
  492.  
  493.     /* Not disposing of ourselves; return to the caller */
  494.     return noErr;
  495. }
  496.  
  497. static void TaskLife( void )
  498. {
  499.     /* TaskLife is the task's life cycle */
  500.  
  501.     /* We are now running a task */
  502.     gTaskMgrFlags |=  tasksRunning;
  503.  
  504.     /* Call the task procedure */
  505.     ( *gCurrentTask.taskProc )( gCurrentTask.taskRefCon );
  506.  
  507.     /* Delete the task */
  508.     DisposeTask( gCurrentTask.taskRefNum );
  509. }
  510.  
  511. OSErr NewTask( TaskProcPtr taskProc, TaskProcPtr taskTermProc,
  512.                 long taskRefCon, short *taskRefNum )
  513. {
  514.     OSErr                        err;
  515.     OSErr                        status;
  516.     TaskRecord                    saveTask;
  517.  
  518.  
  519.     /* Make a backup copy of the current task */
  520.     saveTask = gCurrentTask;
  521.  
  522.     /* Initialize the task record */
  523.     gCurrentTask.taskProc = taskProc;
  524.     gCurrentTask.taskTermProc = taskTermProc;
  525.     gCurrentTask.taskRefCon = taskRefCon;
  526.     gCurrentTask.taskRefNum = gNextTaskRefNum++;
  527.     gCurrentTask.taskFlags = 0;
  528.     gCurrentTask.taskEnvironment.envStack = 0;
  529.  
  530.     /* Give task refNum back to caller */
  531.     if( taskRefNum )
  532.         *taskRefNum = gCurrentTask.taskRefNum;
  533.  
  534.     /* Initialize the task's environment */
  535.     status = SaveEnvironment( &gCurrentTask.taskEnvironment );
  536.     if( status < noErr ) {
  537.         err = status;
  538.         goto exitNewTask;
  539.     }
  540.  
  541.     if( status > saveEnvironment ) {
  542.         TaskLife();
  543.         /* Never to return╔ */
  544.     }
  545.  
  546.     /* Add task to task list */
  547.     err = PtrAndHand( &gCurrentTask, ( Handle )gTaskList, sizeof( TaskRecord ));
  548.     if( err != noErr ) {
  549.  
  550.         /* Dispose of the stack */
  551.         DisposHandle( gCurrentTask.taskEnvironment.envStack );
  552.  
  553.         /* Not enough memory to add it to the task list */
  554.         goto exitNewTask;
  555.     }
  556.     ( *gTaskList )->numTasks++;
  557.  
  558.     /* All dressed up and nowhere to go */
  559.     err = noErr;
  560.  
  561. exitNewTask:
  562.     gCurrentTask = saveTask;
  563.     return err;
  564. }
  565.